#ifndef LIST_H
#define LIST_H

#include "iterator.hh"
#include "memory.hh"
#include "tsddef.hh"

#include <list>
std::list<int> l;

namespace tinystl
{
template <typename T>
class _ListNode
{
  public:
    _ListNode() : next(this), prev(this), data(nullptr) {}

    void hook(_ListNode* node) noexcept
    {
        this->prev = node->prev;
        this->next = node;
        this->prev->next = this;
        this->next->prev = this;
    }

    void unhook() noexcept
    {
        this->prev->next = this->next;
        this->next->prev = this->prev;
    }

    _ListNode* next;
    _ListNode* prev;
    T* data;
};

template <typename T>
class _ListIterator
{
  public:
    using difference_type = ptrdiff_t;
    using iterator_type = tinystl::bidirectional_iterator_tag;
    using value_type = T;
    using pointer = T*;
    using reference = T&;

    using _Node = _ListNode<T>;

    _ListIterator() noexcept : _node() {}
    explicit _ListIterator(_Node* node) noexcept : _node(node) {}

    _Node* _getNode()
    {
        return _node;
    }

    reference operator*() const noexcept
    {
        return *_node->data;
    }

    pointer operator->() const noexcept
    {
        return _node->data;
    }

    _ListIterator& operator++() noexcept
    {
        _node = _node->next;
        return *this;
    }

    _ListIterator operator++(int) noexcept
    {
        _ListIterator tmp(*this);
        _node = _node->next;
        return tmp;
    }

    _ListIterator& operator--() noexcept
    {
        _node = _node->prev;
        return *this;
    }

    _ListIterator operator--(int) noexcept
    {
        _ListIterator tmp(*this);
        _node = _node->prev;
        return tmp;
    }

    bool operator==(const _ListIterator& rhs)
    {
        return _node == rhs._node;
    }

    bool operator!=(const _ListIterator& rhs)
    {
        return _node != rhs._node;
    }

    _Node* _node;
};

template <typename T>
class _ListConstIterator
{
  public:
    using difference_type = ptrdiff_t;
    using iterator_type = tinystl::bidirectional_iterator_tag;
    using value_type = T;
    using pointer = const T*;
    using reference = const T&;

    using _Node = const _ListNode<T>;
    using _Iterator = _ListIterator<T>;

    _ListConstIterator() noexcept : _node() {}
    explicit _ListConstIterator(const _Node* node) noexcept : _node(node) {}
    explicit _ListConstIterator(const _Iterator& iter) noexcept
        : _Node(iter._getNode())
    {
    }

    reference operator*() const noexcept
    {
        return *_node->data;
    }

    pointer operator->() const noexcept
    {
        return _node->data;
    }

    _ListConstIterator& operator++() noexcept
    {
        _node = _node->next;
        return *this;
    }

    _ListConstIterator operator++(int) noexcept
    {
        _ListConstIterator tmp(*this);
        _node = _node->next;
        return tmp;
    }

    _ListConstIterator& operator--() noexcept
    {
        _node = _node->prev;
        return *this;
    }

    _ListConstIterator operator--(int) noexcept
    {
        _ListConstIterator tmp(*this);
        _node = _node->prev;
        return tmp;
    }

    bool operator==(const _ListConstIterator& rhs) noexcept
    {
        return _node == rhs._node;
    }

    bool operator!=(const _ListConstIterator& rhs) noexcept
    {
        return _node != rhs._node;
    }

    _Node* _node;
};

template <typename T, typename Allocator = tinystl::allocator<T>>
class list
{
  public:
    using value_type = T;
    using allocator_type = Allocator;
    using size_type = tinystl::size_t;
    using difference_type = tinystl::ptrdiff_t;
    using reference = value_type&;
    using const_reference = const value_type&;
    using pointer = value_type*;
    using const_pointer = const value_type*;
    using iterator = _ListIterator<T>;
    using const_iterator = _ListConstIterator<T>;

  private:
    using _Node = _ListNode<T>;
    using _NodeAllocator = tinystl::allocator<_Node>;

  public:
    list() : _node(), _size(0)
    {
        _init();
    }

    iterator begin()
    {
        return iterator(_node.next);
    }

    const_iterator cbegin()
    {
        return const_iterator(_node.next);
    }

    iterator end()
    {
        return iterator(&_node);
    }

    const_iterator cend()
    {
        return const_iterator(&_node);
    }

    reference front()
    {
        return *begin();
    }

    reference back()
    {
        return *iterator(_node.prev);
    }

    bool empty() const
    {
        return _node.next == &_node;
    }

    size_type size() const noexcept
    {
        return _size;
    }

    void clear() noexcept
    {
        _Node* curr = _node.next;
        _NodeAllocator nodeAlloc;
        Allocator dataAlloc;
        while (curr != &_node) {
            _Node* tmp = curr;
            curr = tmp->next;
            dataAlloc.deallocate(tmp->data, 1);
            nodeAlloc.deallocate(tmp, 1);
        }
        _init();
    }

    iterator insert(const_iterator pos, const T& value)
    {
        // return iterator(_insert(pos._getNode(), value));
        _Node* curr = _createNode(value);
        curr->hook(pos);
        _size++;
        return iterator(curr);
    }

    template <typename... Args>
    iterator emplace(const_iterator pos, Args&&... args)
    {
        return iterator(_insert(pos._getNode(), std::forward<Args>(args)...));
    }

    iterator erase(const_iterator pos)
    {
        _Node* curr = pos._getNode();
        if (curr == &_node) {
            return end();
        }

        _Node* next = _erase(curr);
        return iterator(next);
    }

    void push_back(const T& value)
    {
        _insert(&_node, value);
    }

    void push_back(T&& value)
    {
        _insert(&_node, std::move(value));
    }

    template <typename... Args>
    void emplace_back(Args&&... args)
    {
        _insert(_node, std::forward<Args>(args)...);
    }

    void pop_back()
    {
        _erase(_node.prev);
    }

    void push_front(const T& value)
    {
        _insert(_node.next, value);
    }

    template <typename... Args>
    void emplace_front(Args&&... args)
    {
        _insert(_node.next, std::forward<Args>(args)...);
    }

    void pop_front()
    {
        _erase(_node.next);
    }

    _Node _node;
    size_type _size;

    void _init()
    {
        _node.next = &_node;
        _node.prev = &_node;
        _node.data = nullptr;
    }

    template <typename... Args>
    _Node* _createNode(Args&&... args)
    {
        _NodeAllocator nodeAlloc;
        Allocator dataAlloc;
        _Node* curr = nodeAlloc.allocate(1);
        curr->data = dataAlloc.allocate(1);
        dataAlloc.construct(curr->data, std::forward<Args>(args)...);
        return curr;
    }

    void _destroyNode(_Node* curr)
    {
        _NodeAllocator nodeAlloc;
        Allocator dataAlloc;
        dataAlloc.destroy(curr->data);
        dataAlloc.deallocate(curr->data, 1);
        nodeAlloc.deallocate(curr, 1);
    }

    template <typename... Args>
    _Node* _insert(_Node* pos, Args&&... args)
    {
        _Node* curr = _createNode(std::forward<Args>(args)...);
        curr->hook(pos);
        _size++;
        return curr;
    }

    _Node* _erase(_Node* curr)
    {
        _Node* next = curr->next;
        curr->unhook();
        _destroyNode(curr);
        _size--;
        return next;
    }
};

}; // namespace tinystl

#endif /* LIST_H */
